#pragma once

#include "VirtualChannel.h"
#include "ClientChannelCore.h"
#include "ConectionEventHandler.h"
#include "DataEventHandler.h"

namespace rdc_transport
{
    namespace Client
    {
        template<class WriteStrategy_t, class ReadStrategy_t>
        class ChannelBuilder : private boost::noncopyable
        {
        private:
            typedef ClientChannelCore<ConectionEventHandler, DataEventHandler> ChCore;
            typedef CVirtualChannel<ChCore, ReadStrategy_t, WriteStrategy_t> VirtChannel;

        public:
            ChannelBuilder(ULONG options)
                : options_(options)
            {}
            std::auto_ptr<IVirtChannel> GetResult(const char* szChannelName, PCHANNEL_ENTRY_POINTS pEntryPoints, IClientVirtChannelCallback* pCallback, IReadHandler* pReadHandler);

        private:
            std::auto_ptr<ConectionEventHandler> CreateConnectionHandler(IClientVirtChannelCallback* pCallback);
            std::auto_ptr<DataEventHandler> CreateDataHandler(ReadStrategy_t* rs, WriteStrategy_t* ws);
            std::auto_ptr<ChCore> CreateCore(const char* szChannelName, PCHANNEL_ENTRY_POINTS pEntryPoints, IClientVirtChannelCallback* pCallback, ReadStrategy_t* rs, WriteStrategy_t* ws);

        private:
            ULONG options_;
        };

        //////////////////////////////////////////////////////////////////////////
        template<class WriteStrategy_t, class ReadStrategy_t>
        std::auto_ptr<ClientChannelCore<ConectionEventHandler, DataEventHandler>> ChannelBuilder<WriteStrategy_t, ReadStrategy_t>::CreateCore(
            const char* szChannelName, 
            PCHANNEL_ENTRY_POINTS pEntryPoints,
            IClientVirtChannelCallback* pCallback,
            ReadStrategy_t* rs, 
            WriteStrategy_t* ws )
        {
            assert(rs);
            assert(ws);

            ChCore::CreatinonParams creationOptions(szChannelName, options_, pEntryPoints);
            std::auto_ptr<ChCore> chCore(new ChCore(creationOptions, CreateConnectionHandler(pCallback), CreateDataHandler(rs, ws)));
            return chCore;
        }

        template<class WriteStrategy_t, class ReadStrategy_t>
        std::auto_ptr<DataEventHandler> ChannelBuilder<WriteStrategy_t, ReadStrategy_t>::CreateDataHandler(ReadStrategy_t* rs, WriteStrategy_t* ws)
        {
            assert(rs);
            assert(ws);

            std::auto_ptr<DataEventHandler> dataHandler(new DataEventHandler());

            dataHandler->RegisterWriter(
                boost::bind(&WriteStrategy_t::WriteComplete, ws, _1, _2, _3, _4),
                boost::bind(&WriteStrategy_t::WriteCancelled, ws, _1, _2, _3, _4)
                );

            dataHandler->RegisterReader(
                boost::bind(&ReadStrategy_t::OnData, rs, _1, _2, _3, _4)
                );
            return dataHandler;
        }

        template<class WriteStrategy_t, class ReadStrategy_t>
        std::auto_ptr<ConectionEventHandler> ChannelBuilder<WriteStrategy_t, ReadStrategy_t>::CreateConnectionHandler(IClientVirtChannelCallback* pCallback)
        {
            std::auto_ptr<ConectionEventHandler> connectionHandler(new ConectionEventHandler());

            connectionHandler->RegisterHandlers(
                boost::bind(&IClientVirtChannelCallback::OnConnect, pCallback),
                boost::bind(&IClientVirtChannelCallback::OnDisconnect, pCallback),
                boost::bind(&IClientVirtChannelCallback::OnTerminate, pCallback)
                );

            return connectionHandler;
        }

        template<class WriteStrategy_t, class ReadStrategy_t>
        std::auto_ptr<IVirtChannel> ChannelBuilder<WriteStrategy_t, ReadStrategy_t>::GetResult(
            const char* szChannelName, 
            PCHANNEL_ENTRY_POINTS pEntryPoints,
            IClientVirtChannelCallback* pCallback,
            IReadHandler* pReadHandler)
        {
            std::auto_ptr<ReadStrategy_t> reader(new ReadStrategy_t);
            std::auto_ptr<WriteStrategy_t> writer(new WriteStrategy_t);

            std::auto_ptr<ChCore> core = CreateCore(szChannelName, pEntryPoints, pCallback, reader.get(), writer.get());
            std::auto_ptr<VirtChannel> virtualChannel(new VirtChannel(
               pReadHandler, 
               core,
               reader, writer)
               );
            
            return virtualChannel; 
        }
    }
}